#include "fileutil.h"
#include <sys/time.h>
#include <sys/stat.h>
#if defined(__linux__)
#include <sys/mman.h>
#endif
#include <sys/types.h>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>




/******************************************************************
 * 功能：创建文件夹
 * 输入参数：
 *      strFolderPath：文件夹全路径
 * 返回值：
 *      bool：创建结果，true - 成功，false - 失败
 * ****************************************************************/
bool createDirectory(const std::string &strFolderPath)
{
    bool bRet = true;
    // 文件/目录不存在，info.st_mode & S_IFDIR为目录
    std::string command = std::string("mkdir -p ") + strFolderPath; // -p表示可以创建多级目录
    system(command.data());
    return bRet;
}

/******************************************************************
 * 功能：删除文件夹
 * 输入参数：
 *      strFolderPath：文件夹全路径
 * ****************************************************************/
void deleteDirectory(const std::string &strFolderPath)
{
    std::string command = std::string("rm -rf ") + strFolderPath;
    system(command.data());
    return;
}

/******************************************************************
 * 功能：创建文件
 * 输入参数：
 *      strFilePath：文件全路径
 * 返回值：
 *      bool：创建结果，true - 成功，false - 失败
 * ****************************************************************/
bool createFile(const std::string& strFilePath)
{
    bool bRet = false;

    int fd = open(strFilePath.data(), O_RDWR | O_CREAT | O_TRUNC, 0777);
    if (0 <= fd)
    {
        bRet = true;
        close(fd);
    }

    return bRet;
}

/******************************************************************
 * 功能：删除文件
 * 输入参数：
 *      strFilePath：文件全路径
 * ****************************************************************/
void deleteFile(const std::string& strFilePath)
{
    std::string command = std::string("rm -rf ") + strFilePath;
    system(command.data());
    return;
}

/******************************************************************
 * 功能：遍历文件夹下所有文件信息，暂不支持多级目录
 * 输入参数：
 *      strFileFolder：文件夹全路径
 * 输出参数：
 *      std::vector：文件信息集合
 * ****************************************************************/
void getFilePaths(const std::string &strFileFolder, std::vector<FileAbsInfo> &vtFileInfos)
{
    DIR *pDir = opendir(strFileFolder.data());

    if (!pDir)
    {
        printf("file folder (%s) doesn't exist!\r\n", strFileFolder.data());
        return;
    }

    struct dirent *ptr = nullptr;
    FileAbsInfo stFileInfo;

    while (0 != (ptr = readdir(pDir)))
    {
        if (0 != strcmp(ptr->d_name, ".") && 0 != strcmp(ptr->d_name, ".."))
        {
            stFileInfo.strName = std::string(ptr->d_name);
            stFileInfo.strPath = strFileFolder + "/" + stFileInfo.strName;
            vtFileInfos.emplace_back(stFileInfo);
        }
    }

    closedir(pDir);

    return;
}

/******************************************************************
 * 功能：内容追加入文件
 * 输入参数：
 *      strFilePath：文件全路径
 *      strContent：加入的文件内容
 * ****************************************************************/
void appendFile(const std::string &strFilePath, const std::string &strContent)
{
    std::ofstream fout;
    fout.open(strFilePath, std::ios::app | std::ios::binary | std::ios::out);

    if (!(fout.is_open()))
    {
        printf("open file (%s) failed.\r\n", strFilePath.data());
        return;
    }

    fout << strContent;
    fout.flush();
    fout.close();

    return;
}

/******************************************************************
 * 功能：内容写入文件，文件没有时自动创建
 * 输入参数：
 *      strFilePath：文件全路径
 *      strContent：写入的文件内容
 * ****************************************************************/
void writeFile(const std::string &strFilePath, const std::string &strContent)
{
#if defined(__linux__)
    // 效率ok
    int fd = open(strFilePath.data(), O_RDWR | O_CREAT | O_TRUNC, 0777);
    if (0 <= fd)
    {
        size_t len = strContent.length();
        truncate(strFilePath.data(), len);
        void *mp = mmap(NULL, len, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
        if (mp)
        {
            memcpy(mp, strContent.data(), len);
            munmap(mp, len);
        }

        close(fd);
    }
#elif defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)
    // 效率差不多
    std::ofstream fout(strFilePath, /*std::ios::app | */std::ios::binary | std::ios::out); // 需要指定模式，否则存在数据丢失问题
    if (!(fout.is_open()))
    {
        printf("open file (%s) failed.\r\n", strFilePath.data());
        return;
    }

    fout << strContent;
    fout.flush();
    fout.close();
#else
    // 效率差不多
    FILE *fp = fopen(strFilePath.data(), "wb+"); // 注意只用w或者wb时，存在数据丢失问题，建议用wb+
    if (fp)
    {
        // fputs(strContent.data(), fp);
        fwrite(strContent.data(), strContent.size(), 1, fp);
        fflush(fp);
        fclose(fp);
    }
#endif
    return;
}

/******************************************************************
 * 功能：读出文件内容
 * 输入参数：
 *      strFilePath：文件全路径
 * 输出参数：
 *      strContent：文件内容
 * ****************************************************************/
void readFile(const std::string &strFilePath, std::string &strContent)
{
    std::ifstream fin(strFilePath);
    if (!(fin.is_open()))
    {
        printf("open file (%s) failed.\r\n", strFilePath.data());
        return;
    }
#if 0
    // 该方法效率一般，慎用
    std::istreambuf_iterator<char> iterBeg(fin), iterEnd;
    strContent = std::string(iterBeg, iterEnd);
#else
    // 该方法效率较一般读取方法快20倍
    std::vector<char> buffer;
    buffer.clear();
    buffer.resize(fin.seekg(0, std::ios::end).tellg());
    fin.seekg(0, std::ios::beg).read(&buffer[0], static_cast<std::streamsize>(buffer.size()));
    strContent = std::string(buffer.data(), buffer.size());
#endif
    fin.close();

    return;
}
